package com.snsoft.config;

import java.beans.PropertyVetoException;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.snsoft.aspect.LogAspect;

/**
 * 
 * @author 慎伟康
 * 
 * @date 2017年11月26日 上午12:16:09
 * 
 * @Description TODO
 *    Spring根配置
 * 
 * @Configuration
 *    声明配置类 
 * 
 * @ComponentScan
 *    声明自动扫描，会自动扫描基础包下带注解的类，如@Controller、@Service、@Repository等
 * @see basePackages
 *    扫描的基础包，这种方法不是类型安全的。
 * @see excludeFilters
 *    扫描排除过滤，由于有两个配置类，且都开启自动扫描，若扫描的包有重复，则重复包中带注解的类会被创建两次，
 *    解决方法为springMVC配置类只扫描与web相关的类：@Controller和@ControllerAdvice，
 *    而springRoot配置类不扫描带@Controller和@ControllerAdvice的类。
 *    
 * @PropertySource
 *    声明属性源
 *    
 * @EnableAspectJAutoProxy
 *    启用Aspect自动代理，用于切面AOP
 *    
 * @EnableTransactionManagement
 *    启用注解式事务，通知spring，@Transactional注解的类或方法被事务的切面包围。
 *    需要aop相关jar支持，没加入aop相关jar之前，测试事务一直失败！
 */
@Configuration
@ComponentScan(basePackages = { "com.snsoft" }, excludeFilters = {
	@Filter(type = FilterType.ANNOTATION, value = {
	EnableWebMvc.class, Controller.class, ControllerAdvice.class }) })
@PropertySource(value = { "classpath:/db.properties" })
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringRootConfig {
	
	/** 与属性装配相关的对象，用来获取属性文件的值，spring特有，暂时用这种方法，后期可能会更换。 */
//	@Autowired
//	Environment env;

	/**
	 * 配置这个bean就能使用占位符，方便属性注入。
	 * @return PropertySourcesPlaceholderConfigurer
	 */
	@Bean
	public static PropertySourcesPlaceholderConfigurer configurer(){
		return new PropertySourcesPlaceholderConfigurer();
	}
	
	/**
	 * 配置一个DataSource，放弃XML配置方式。
	 * @Bean 注解的方法可以采用任何必要的Java功能来产生bean实例
	 *     name的值为bean的id，若不指定，则默认为方法名。
	 * @Value 用于注入值
	 * @return ComboPooledDataSource
	 */
	@Bean(name="dataSource")
	public DataSource dataSource(
			@Value("${jdbc.driverClass}") String driverClass,
			@Value("${jdbc.url}") String url,
			@Value("${jdbc.user}") String user,
			@Value("${jdbc.password}") String password){
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		
		try {
			//设置四个基本属性，从属性文件获取值
			dataSource.setDriverClass(driverClass);
			dataSource.setJdbcUrl(url);
			dataSource.setUser(user);
			dataSource.setPassword(password);
		} catch (PropertyVetoException e) {
			e.printStackTrace();
		}
		return dataSource;
	}
//	@Bean(name="dataSource")
//	public DataSource dataSource(){
//		ComboPooledDataSource dataSource = new ComboPooledDataSource();
//		
//		try {
//			//设置四个基本属性，从属性文件获取值
//			dataSource.setDriverClass(env.getProperty("jdbc.driverClass"));
//			dataSource.setJdbcUrl(env.getProperty("jdbc.url"));
//			dataSource.setUser(env.getProperty("jdbc.user"));
//			dataSource.setPassword(env.getProperty("jdbc.password"));
//		} catch (PropertyVetoException e) {
//			e.printStackTrace();
//		}
//		return dataSource;
//	}
	
	/**
	 * 声明一个事务管理器，可以有多种实现。若实现多种事务管理器，则在声明事务时需指明使用哪个事务管理器。
	 * @return
	 */
	@Bean
	public PlatformTransactionManager transactionManager(DataSource dataSource){
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		transactionManager.setDataSource(dataSource);
		return transactionManager;
	}
	
	/**
	 * 配置一个JdbcTemplate，dao中可注入使用。
	 * @return JdbcTemplate
	 */
	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource){
		return new JdbcTemplate(dataSource);
	}
	
	/**
	 * 配置一个QueryRunner，dao中可注入使用。
	 * @return QueryRunner
	 */
	@Bean
	public QueryRunner queryRunner(DataSource dataSource){
		return new QueryRunner(dataSource);
	}
	
	/**
	 * 配置aop切面，@Aspect不会被自动扫描，所以需要手动配置
	 * @return LogAspect
	 */
	@Bean
	public LogAspect logAspect(){
		return new LogAspect();
	}
}
